DeriveGamemode("frettabox_base")
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "tables.lua" )
AddCSLuaFile( "admin.lua" )
AddCSLuaFile( "inventories.lua" )
AddCSLuaFile( "awards.lua" )
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_postprocess.lua" )
AddCSLuaFile( "cl_spawnmenu.lua" )
AddCSLuaFile( "cl_scoreboard.lua" )
AddCSLuaFile( "cl_hudpickup.lua" )
AddCSLuaFile( "cl_deathnotice.lua" )
AddCSLuaFile( "cl_notice.lua" )
AddCSLuaFile( "cl_hudstains.lua" )

include( "tables.lua" )
include( "admin.lua" )
include( "inventories.lua" )
include( "awards.lua" )
include( "shared.lua" )
include( "player.lua" )
include( "ply_extension.lua" )
include( "powerups.lua" )
include( "anims.lua" )

if GameInfo.MusicEnabled then
	resource.AddFile("sound/"..GameInfo.LastMinuteSong)
end

for k,v in pairs({4,6,8}) do
	resource.AddFile("models/Zed/malezed_0"..v..".dx80.vtx")
	resource.AddFile("models/Zed/malezed_0"..v..".dx90.vtx")
	resource.AddFile("models/Zed/malezed_0"..v..".mdl")
	resource.AddFile("models/Zed/malezed_0"..v..".phy")
	resource.AddFile("models/Zed/malezed_0"..v..".sw.vtx")
	resource.AddFile("models/Zed/malezed_0"..v..".vvd")
end
resource.AddFile("models/Zed/male_shared.mdl")

resource.AddFile("materials/models/Zed/Male/pupil_l.vmt")
resource.AddFile("materials/models/Zed/Male/pupil_l.vtf")
resource.AddFile("materials/models/Zed/Male/pupil_r.vmt")
resource.AddFile("materials/models/Zed/Male/pupil_r.vtf")
resource.AddFile("materials/models/Zed/Male/eyeball_l.vmt")
resource.AddFile("materials/models/Zed/Male/eyeball_l.vtf")
resource.AddFile("materials/models/Zed/Male/eyeball_r.vmt")
resource.AddFile("materials/models/Zed/Male/eyeball_r.vtf")
resource.AddFile("materials/models/Zed/Male/dark_eyeball_l.vmt")
resource.AddFile("materials/models/Zed/Male/dark_eyeball_l.vtf")
resource.AddFile("materials/models/Zed/Male/dark_eyeball_r.vmt")
resource.AddFile("materials/models/Zed/Male/dark_eyeball_r.vtf")
resource.AddFile("materials/models/Zed/Male/glint.vmt")
resource.AddFile("materials/models/Zed/Male/glint.vtf")
resource.AddFile("materials/models/Zed/Male/mouth.vmt")
resource.AddFile("materials/models/Zed/Male/mouth.vtf")
resource.AddFile("materials/models/Zed/Male/citizen_sheet.vmt")
resource.AddFile("materials/models/Zed/Male/citizen_sheet.vtf")
resource.AddFile("materials/models/Zed/Male/blood_sheet.vmt")
resource.AddFile("materials/models/Zed/Male/blood_sheet.vtf")
resource.AddFile("materials/models/Zed/Male/grey_sheet.vmt")
resource.AddFile("materials/models/Zed/Male/grey_sheet.vtf")
resource.AddFile("materials/models/Zed/Male/plaid_sheet.vmt")
resource.AddFile("materials/models/Zed/Male/plaid_sheet.vtf")
resource.AddFile("materials/models/Zed/Male/service_sheet.vmt")
resource.AddFile("materials/models/Zed/Male/service_sheet.vtf")
resource.AddFile("materials/models/Zed/Male/jackson_sheet.vmt")
resource.AddFile("materials/models/Zed/Male/jackson_sheet.vtf")
resource.AddFile("materials/models/Zed/Male/eric_facemap.vmt")
resource.AddFile("materials/models/Zed/Male/eric_facemap.vtf")
resource.AddFile("materials/models/Zed/Male/joe_facemap.vmt")
resource.AddFile("materials/models/Zed/Male/joe_facemap.vtf")
resource.AddFile("materials/models/Zed/Male/sandro_facemap.vmt")
resource.AddFile("materials/models/Zed/Male/sandro_facemap.vtf")
resource.AddFile("materials/models/Zed/Male/vance_facemap.vmt")
resource.AddFile("materials/models/Zed/Male/vance_facemap.vtf")
for i=1,4 do
	for e=1,5 do
		resource.AddFile("materials/models/Zed/Male/g"..i.."_0"..e.."_sheet.vmt")
	end
end

resource.AddFile("resource/fonts/typenoksidi.ttf")
resource.AddFile("resource/fonts/mortbats.ttf")

resource.AddFile("materials/gmod/scope.vtf")
resource.AddFile("materials/gmod/scope.vmt")
resource.AddFile("materials/gmod/scope-refract.vmt")
resource.AddFile("materials/gmod/scope-refract.vtf")

resource.AddFile("materials/blood/Blood1.vmt")
resource.AddFile("materials/blood/Blood2.vmt")
resource.AddFile("materials/blood/Blood3.vmt")
resource.AddFile("materials/blood/Blood4.vmt")
resource.AddFile("materials/blood/Blood5.vmt")
resource.AddFile("materials/blood/Blood6.vmt")
resource.AddFile("materials/blood/Blood7.vmt")
resource.AddFile("materials/blood/Blood8.vmt")

resource.AddFile("materials/ReDead/allyvision.vmt")
resource.AddFile("materials/ReDead/allyvisionblue.vtf")

resource.AddFile("models/Zed/weapons/v_undead.vvd")
resource.AddFile("models/Zed/weapons/v_undead.mdl")
resource.AddFile("models/Zed/weapons/v_undead.sw.vtx")
resource.AddFile("models/Zed/weapons/v_undead.dx80.vtx")
resource.AddFile("models/Zed/weapons/v_undead.dx90.vtx")
resource.AddFile("models/Zed/weapons/v_wretch.vvd")
resource.AddFile("models/Zed/weapons/v_wretch.mdl")
resource.AddFile("models/Zed/weapons/v_wretch.sw.vtx")
resource.AddFile("models/Zed/weapons/v_wretch.dx80.vtx")
resource.AddFile("models/Zed/weapons/v_wretch.dx90.vtx")
resource.AddFile("models/Zed/weapons/v_banshee.vvd")
resource.AddFile("models/Zed/weapons/v_banshee.mdl")
resource.AddFile("models/Zed/weapons/v_banshee.sw.vtx")
resource.AddFile("models/Zed/weapons/v_banshee.dx80.vtx")
resource.AddFile("models/Zed/weapons/v_banshee.dx90.vtx")
resource.AddFile("models/Zed/weapons/v_disease.vvd")
resource.AddFile("models/Zed/weapons/v_disease.mdl")
resource.AddFile("models/Zed/weapons/v_disease.sw.vtx")
resource.AddFile("models/Zed/weapons/v_disease.dx80.vtx")
resource.AddFile("models/Zed/weapons/v_disease.dx90.vtx")
resource.AddFile("models/Zed/weapons/v_ghoul.vvd")
resource.AddFile("models/Zed/weapons/v_ghoul.mdl")
resource.AddFile("models/Zed/weapons/v_ghoul.sw.vtx")
resource.AddFile("models/Zed/weapons/v_ghoul.dx80.vtx")
resource.AddFile("models/Zed/weapons/v_ghoul.dx90.vtx")

resource.AddFile("models/Weapons/v_shot_m3super91.dx80.vtx")
resource.AddFile("models/Weapons/v_shot_m3super91.dx90.vtx")
resource.AddFile("models/Weapons/v_shot_m3super91.sw.vtx")
resource.AddFile("models/Weapons/v_shot_m3super91.vvd")
resource.AddFile("models/Weapons/v_shot_m3super91.mdl")

resource.AddFile("materials/Models/Weapons/v_models/shot_m3super91/shot_m3super91.vmt")
resource.AddFile("materials/Models/Weapons/v_models/shot_m3super91/shot_m3super91.vtf")
resource.AddFile("materials/Models/Weapons/v_models/shot_m3super91/shot_m3super91_norm.vtf")
resource.AddFile("materials/Models/Weapons/v_models/shot_m3super91/shot_m3super91_norm.vmt")
resource.AddFile("materials/Models/Weapons/v_models/shot_m3super91/shot_m3super91_2.vtf")
resource.AddFile("materials/Models/Weapons/v_models/shot_m3super91/shot_m3super91_2.vmt")
resource.AddFile("materials/Models/Weapons/v_models/shot_m3super91/shot_m3super91_2_norm.vtf")
resource.AddFile("materials/Models/Weapons/v_models/shot_m3super91/shot_m3super91_2_norm.vmt")

////////////////////////////////////////////////////////////////////

function GM:Initialize( )

	self.BaseClass:Initialize( )
	self:SetupTeams()

	game.ConsoleCommand("sv_maxvelocity 9999\n")
	game.ConsoleCommand("sk_dynamic_resupply_modifier 0\n")
	game.ConsoleCommand("mapcyclefile "..GameInfo.CycleFile.."\n")

	GameInfo.Disconnected = {}
	
	GameInfo.HumanSpawnTime = CurTime() + GameInfo.DeadLine*60
	GameInfo.TimeLimit = GameInfo.TimeLimit*60
	
	GameInfo.SurviveTimeRatio = 0
	GameInfo.GameOver = false
	GameInfo.EndTime = CurTime() + GameInfo.TimeLimit
	GameInfo.Unlock = .2
	
	GameInfo.NpcInterval = CurTime() + 25
	
	if GameInfo.RealisticFallDamage then
		game.ConsoleCommand("mp_falldamage 1\n")
	else
		game.ConsoleCommand("mp_falldamage 0\n")
	end
	
	timer.Create("LastMinute", GameInfo.TimeLimit - 60, 1, GAMEMODE.LastMinute, GAMEMODE)
	timer.Create("CheckTimer", GameInfo.TimeLimit * .2, 4, GAMEMODE.CheckTime, GAMEMODE)
end

function GM:LastMinute()

	for k,v in pairs(player.GetAll()) do
		if v:Team() == TEAM_HUMANS then
			if GameInfo.MusicEnabled then
				v:ClientSound(GameInfo.LastMinuteSong)
			end
			Inventory.Notice(v, "One minute left", Inventory.White)
		end
	end
end

function GM:CheckTime()
	GameInfo.Unlock = GameInfo.Unlock + .2
	for k,v in pairs(team.GetPlayers(TEAM_HUMANS)) do
		v:VoiceSound(ChooseRandom(v.Alert),100,math.Rand(3,5))
		v:ClientSound(ChooseRandom(GameSounds.AmbientScream))
		umsg.Start("UnlockClass", v)
		umsg.Float(GameInfo.Unlock)
		umsg.End() 
		local percent = GameInfo.Unlock * 100
		Inventory.Notice(v, percent.."% mortality", Inventory.White)
		if percent == 40 then
			timer.Simple(4, function(v) Inventory.Notice(v, "The mortality rate is a warning", Inventory.White) end, v )
			timer.Simple(6, function(v) Inventory.Notice(v, "Each time it increases, more zombies spawn", Inventory.White) end, v )
			timer.Simple(8, function(v) Inventory.Notice(v, "New zombie types will also appear", Inventory.White) end, v )
		end
	end
	for k,v in pairs(team.GetPlayers(TEAM_UNDEAD)) do
		v:VoiceSound(ChooseRandom(v.Taunt),100,math.Rand(0,3))
		umsg.Start("UnlockClass", v)
		umsg.Float(GameInfo.Unlock)
		umsg.End() 
		Inventory.Notice(v, "New classes unlocked", Inventory.Green)
	end
	if team.NumPlayers(TEAM_HUMANS) < 1 then
		GAMEMODE:EndGame()
	end
end

function GM:ChangeCureLocation( noPunish )
	if GameInfo.EndTime < CurTime() + 120 then return end
	local oldpos = Vector(0,0,0)
	if GameInfo.Cure then
		oldpos = GameInfo.Cure:GetPos()
		GameInfo.Cure:Remove()
		GameInfo.Cure = nil
	end
	
	local effectdata = EffectData()
	effectdata:SetOrigin(oldpos)
	util.Effect("antidote_die", effectdata)
	
	local distance = 1
	local newpos = Vector(0,0,0)
	local ent
	while distance < 1500 do
		ent = ChooseRandom(ents.FindByClass("prop_physics*"))
		newpos = ent:GetPos()
		distance = oldpos:Distance(newpos)
		local pos = ent:LocalToWorld(ent:OBBCenter())
		if not util.IsInWorld(pos) then
			distance = 1
		end
	end
	local pos = ent:LocalToWorld(ent:OBBCenter())
	local cure = ents.Create("sent_antidote")
	cure:SetCollisionGroup(COLLISION_GROUP_WEAPON)
	cure:Spawn()
	cure:SetPos(pos)
	GameInfo.Cure = cure
	
	for k,v in pairs(player.GetAll()) do
		v:SetNetworkedVector("antidotepos",cure:LocalToWorld(cure:OBBCenter()))
		Inventory.Notice(v, "Antidote zone changed", Inventory.White)
		umsg.Start("UpdateCureTime",v)
		umsg.Short( CurTime() + (GameInfo.LocationInterval*60) )
		umsg.End() 
	end
	
	for k,v in pairs(team.GetPlayers(TEAM_HUMANS)) do	
		v:VoiceSound(ChooseRandom(v.Alert),100,math.Rand(.1,1))
		if not noPunish then
			if not v:Immune() then
				v:DoInfection()
				Inventory.Notice(v, "Your immune system failed", Inventory.Red)
				if not v.AntHint then
					v.AntHint = true
					timer.Simple(4, function(v) Inventory.Notice(v, "To cure infections, go to the antidote", Inventory.White) end, v )
					timer.Simple(6, function(v) Inventory.Notice(v, "The antidote zone is marked on your screen", Inventory.White) end, v )
				end
			else
				v:SetImmune(false)
			end
		end
	end
	GameInfo.StartTime = CurTime()
end

function GM:Think()
	for k,v in pairs(player.GetAll()) do
		if v:Alive() then
			v:DoOnFire()
			v:DoPoisoned()
			v:DoInfected()
			v:DoStamina()
			v:DoBleeding()
			v:DoBlindness()
			v:DoDrown()
		end
	end
	if CurTime() > GameInfo.TimeLimit and not GameInfo.GameOver then
		GAMEMODE:EndGame()
	end
	GAMEMODE:SpawnNpcs()
end

function GM:SpawnNpcs()
	if not GameInfo.NpcInterval then
		if not GameInfo.NpcSpawns then
			GameInfo.NpcSpawns = ents.FindByClass( "info_player_counterterrorist" )
			GameInfo.NpcSpawns = table.Add(GameInfo.NpcSpawns, ents.FindByClass( "info_player_rebel" ))
			GameInfo.NpcSpawns = table.Add(GameInfo.NpcSpawns, ents.FindByClass( "info_player_undead" ))
			GameInfo.NpcSpawns = table.Add(GameInfo.NpcSpawns, ents.FindByClass( "info_player_zombie" ))
			if GameInfo.NpcSpawns == nil or table.Count(GameInfo.NpcSpawns) < 2 then
				GameInfo.NpcSpawns = ents.FindByClass( "info_player_human" )
			end
		end
		if team.NumPlayers(TEAM_HUMANS) == 0 then
			GameInfo.NpcInterval = CurTime() + GameInfo.NpcSpawnTime + math.random(-5,5)
			return
		end
		for i=0,team.NumPlayers(TEAM_HUMANS) + math.random(1,3) do
			GAMEMODE:SpawnUndead("npc_undead")
		end
		if GameInfo.Unlock > .2 then
			for i=1,math.random(1,2) do
				GAMEMODE:SpawnUndead("npc_ghoul")
				GAMEMODE:SpawnUndead("npc_undead")
			end
		end
		if GameInfo.Unlock > .4 then
			for i=1,math.Clamp(math.Round(team.NumPlayers(TEAM_HUMANS) / 2),1,8) do
				GAMEMODE:SpawnUndead("npc_wretch")
			end
			GAMEMODE:SpawnUndead("npc_undead")
		end
		if GameInfo.Unlock > .6 then
			GAMEMODE:SpawnUndead("npc_disease")
		end
		GameInfo.NpcInterval = CurTime() + GameInfo.NpcSpawnTime + math.random(-5,5)
	elseif GameInfo.NpcInterval < CurTime() then 
		GameInfo.NpcInterval = nil
	end
end

function GM:SpawnUndead(ztype)
	local zombies = ents.FindByClass("npc_*")
	if table.Count(zombies) > MAX_NUM_ZOMBIES then return end
	
	local point = ChooseRandom(GameInfo.NpcSpawns) 
	local pos = point:GetPos() + Vector(0,0,5)
	
	local block
	for k, ent in pairs(ents.FindInBox(point:GetPos() + Vector(-48, -48, 0), point:GetPos() + Vector(48, 48, 60))) do
		if ent and ent:IsNPC() then
			block = ent
		end
	end
	if block then
		if block:IsValid() then
			GAMEMODE:SpawnUndead(block:GetClass())
			block:Remove()
		end
	end
	
	local undead = ents.Create(ztype)
	undead:SetPos(pos)
	undead:Spawn()
	undead:Activate()
end

function GM:InitPostEntity( )
	GAMEMODE:ChangeCureLocation(true)
	timer.Create("CureTimer", (60*GameInfo.LocationInterval), 0, GAMEMODE.ChangeCureLocation, GAMEMODE)
	if not GameInfo.NpcSpawns then
		GameInfo.NpcSpawns = ents.FindByClass( "info_player_counterterrorist" )
		GameInfo.NpcSpawns = table.Add(GameInfo.NpcSpawns, ents.FindByClass( "info_player_rebel" ))
		GameInfo.NpcSpawns = table.Add(GameInfo.NpcSpawns, ents.FindByClass( "info_player_undead" ))
		GameInfo.NpcSpawns = table.Add(GameInfo.NpcSpawns, ents.FindByClass( "info_player_zombie" ))
		if GameInfo.NpcSpawns == nil or table.Count(GameInfo.NpcSpawns) < 2 then
			GameInfo.NpcSpawns = ents.FindByClass( "info_player_human" )
		end
	end
	GAMEMODE:EditProps()
end

Replace = {}
Replace[1] = {Class="prop_physics", Replace=nil}
Replace[2] = {Class="prop_physics_override", Replace=nil}
Replace[3] = {Class="weapon_*", Replace=Inventory.AmmoItems}
Replace[4] = {Class="item_*", Replace=Inventory.AidItems}
Replace[5] = {Class="npc_*", Replace=Inventory.KitItems}
Replace[6] = {Class="env_fire", Replace=Inventory.KitItems}
Replace[7] = {Class="prop_vehicle*", Replace=Inventory.KitItems}
Replace[8] = {Class="prop_ragdoll", Replace=Inventory.KitItems}
Replace[9] = {Class="point_template", Replace=Inventory.KitItems}

ReplacedModels = {"models/items/boxmrounds.mdl",
"models/items/boxsrounds.mdl",
"models/items/boxbuckshot.mdl",
"models/items/357ammobox.mdl",
"models/items/357ammo.mdl",
"models/items/battery.mdl",
"models/healthvial.mdl"}

function GM:EditProps()
	for k,v in pairs(ReplacedModels) do
		for i=1,math.random(1,2) do
			local amm = ents.Create("prop_physics_respawnable")
			util.PrecacheModel(v)
			amm:SetModel(v)
			amm:SetPos(ChooseRandom(ents.FindByClass("prop_physics")):GetPos() + Vector(0,0,20))
			amm:SetAngles(Angle(0,0,0))
			amm:Spawn()
		end
	end
	for i=1,math.random(3,6) do
		local item = ents.Create("sent_weapon")
		local model = ChooseRandom({"models/weapons/w_pistol.mdl",
		"models/weapons/w_pist_deagle.mdl",
		"models/weapons/w_pist_elite_dropped.mdl",
		"models/weapons/w_smg_mac10.mdl",
		"models/weapons/w_smg_tmp.mdl"})
		util.PrecacheModel(model)
		item:SetModel(model)
		item:SetPos(ChooseRandom(ents.FindByClass("prop_physics")):GetPos() + Vector(0,0,20))
		item:SetAngles(Angle(0,0,0))
		item:Spawn()
	end
	for c,d in pairs(Replace) do
		for k,v in pairs(ents.FindByClass(d.Class)) do
			if not string.find(v:GetClass(),"_multiplayer") then
				timer.Create("PropSpawn"..k.."p", 0.1 * math.random(10,30), 1, GAMEMODE.SpawnItem, GAMEMODE, v:GetModel(), d.Replace, v:GetPos(), v:GetAngles(), v:GetSkin())
				v:Remove()
			end
		end
	end
	local radi = ents.Create("point_radiation")
	radi:SetPos(ChooseRandom(GameInfo.NpcSpawns):GetPos() + Vector(0,0,50))
	radi:Spawn()
	radi:Activate()
end

function GM:SpawnItem(model,replace,pos,angles,skin)
	model = model or "models/healthvial.mdl"
	model = string.Replace(model,"//","/")
	local prop = ents.Create("prop_physics_respawnable")
	prop:SetPos(pos + Vector(0,0,1))
	prop:SetAngles(angles)
	if replace then
		prop:SetModel(ChooseRandom(replace))
	elseif string.find(string.lower(model),"oildrum001_explosive.mdl") or table.HasValue(ReplacedModels,string.lower(model)) then
		prop:SetModel(ChooseRandom(Inventory.KitItems))
	else
		prop:SetModel(model)
	end
	prop:SetSkin(skin)
	prop:Spawn()
	prop:SetHealth(150)
	if not prop:IsValid() then 
		timer.Create("RePropSpawn"..math.random(1,900), 0.1 * math.random(10,30), 1, GAMEMODE.SpawnItem, GAMEMODE, model, replace, pos + Vector(0,0,5), Angle(0,0,0), values)
	end
end

function GM:GetGameDescription()
	if GameInfo.ModdedName == "" then
		if GameInfo.HumanDamageScale > 1.0 or
		GameInfo.HumanDamageScale < 1.0 or
		GameInfo.ZombieItemChance > 50 or
		GameInfo.NpcSpawnTime > 60 or
		Points["Undead"] > 1 or
		Points["Wretch"] > 2 or
		Points["Ghoul"] > 2 then
			GameInfo.ModdedName = "Edited"
		end
	end
	return self.Name.." "..GameInfo.ModdedName
end 

function GM:PlayerInitialSpawn( ply )
	if (team.NumPlayers(TEAM_HUMANS) >= GameInfo.HumanCount and team.NumPlayers(TEAM_UNDEAD) < 1) then
		local chosen = ChooseRandom(team.GetPlayers(TEAM_HUMANS))
		chosen:SetTeam(TEAM_UNDEAD)
		chosen.ZombieClass = "Dead"
		chosen:Kill()
		Inventory.Notice(chosen, "You were chosen to join the undead", Inventory.White)
		chosen:AddPowerup(math.random(1,3))
		chosen:AddAwardPoints(AWARD_BAD_POINTS,100)
		chosen:AddAwardPoints(AWARD_BAD_KILLS,100)
		chosen:AddAwardPoints(AWARD_BAD_HEADSHOT,100)
	end
	self.BaseClass:PlayerInitialSpawn( ply )
	ply.HumanClass = ChooseRandom({"Militant","Support","Medic","Engineer"})
	ply.ZombieClass = "Undead"
	if table.HasValue(GameInfo.Disconnected,ply:SteamID()) 
	or (GameInfo.HumanSpawnTime < CurTime() and team.NumPlayers(TEAM_HUMANS) > 0) then
		if team.NumPlayers(TEAM_UNDEAD) < 1 then
			ply:AddPowerup(math.random(1,3))
		end
		ply:SetTeam(TEAM_UNDEAD)
		ply:SetNetworkedString("available","99")
		umsg.Start("UnlockClass", ply)
		umsg.Float(GameInfo.Unlock)
		umsg.End() 
		umsg.Start("UpdateTime", ply)
		umsg.Short(GameInfo.EndTime)
		umsg.End() 
	else
		local prof = OpenFromFile(string.sub(ply:SteamID(),11))
		if prof[1] then
			ply:SetNetworkedString("classdisable",tostring(prof[1]))
		else
			ply:SetNetworkedString("classdisable",ply.HumanClass)
			timer.Simple(10, function(ply) Inventory.Notice(ply, "Press F1 for a helpful guide", Inventory.White) end, ply )
			timer.Simple(15, function(ply) Inventory.Notice(ply, "Press F4 to open the options menu", Inventory.White) end, ply )
			timer.Simple(20, function(ply) Inventory.Notice(ply, "Press F2 to quick-buy items", Inventory.White) end, ply )
			timer.Simple(25, function(ply) Inventory.Notice(ply, "Press NumPad 0 for voice commands", Inventory.White) end, ply )
		end
		Inventory.UpdateClientStock(ply)
		ply:SetTeam(TEAM_SPECTATOR)
		ply:Spawn()
		ply:Spectate(OBS_MODE_ROAMING)
		ply:SetMoveType(MOVETYPE_NOCLIP)
		ply:SetColor(0,0,0,0)
		ply.MenuOpen = true
		for i=0,3 do
			numpad.OnDown( ply, i, "VoiceChat", i ) 
		end
		//numpad.OnDown( ply, 0, "VoiceChat", 0 ) 
	end
	/* timer.Simple(3,function() umsg.Start("ClassMenu", ply)
		umsg.Short(TEAM_HUMANS)
	umsg.End() end) */
end

function GM:LoadClassMenu(ply)
	umsg.Start("ClassMenu", ply)
	umsg.Short(TEAM_HUMANS)
	umsg.End() 
	umsg.Start("InvSynch", ply)
	umsg.End() 
end

function GM:HumanSetup( ply )
	ply.LastItem = "Pistol Rounds"
	ply:SetColor(255,255,255,255)
	ply:EmptyInventory()
	if ply:IsFemale() then
		for i=1,#HumanClass do
			if (string.lower(HumanClass[i].Name) == string.lower(ply.HumanClass)) then
				GAMEMODE:SetPlayerSpeed( ply, HumanClass[i].Speed, HumanClass[i].Speed )
				ply.WalkSpeed = HumanClass[i].Speed
				ply:SetHealth(HumanClass[i].Health)
				ply.MaxHealth = HumanClass[i].Health
				ply.WeaponSlots = HumanClass[i].WeaponSlots
				ply:SetModel(ChooseRandom(HumanClass[i].F_Model)) 
				ply.Alert = HumanClass[i].F_Alert
				ply.Death = HumanClass[i].F_Death
				ply.Pain = HumanClass[i].F_Pain
				ply.Taunt = HumanClass[i].F_Taunt
				ply.Burn = HumanClass[i].F_Burn
				ply.Pickup = HumanClass[i].F_Pickup
				ply.Cough = HumanClass[i].F_Cough
				ply.Scared = HumanClass[i].F_Scared
				ply.Heal = HumanClass[i].F_Heal
				ply.Reload = HumanClass[i].F_Reload
				ply.Supply = HumanClass[i].F_Supply
				ply:SetInvSlots(HumanClass[i].InventorySlots)
				ply.DeathType = "normal"
				ply.Class = HumanClass[i].Name
				ply:Give("weapon_inventory")
				for k,v in pairs(HumanClass[i].Primaries) do
					ply:Give(v)
				end
				ply:SelectWeapon(HumanClass[i].Primaries[2])
			end
		end
	else
		for i=1,#HumanClass do
			if (string.lower(HumanClass[i].Name) == string.lower(ply.HumanClass)) then
				GAMEMODE:SetPlayerSpeed( ply, HumanClass[i].Speed, HumanClass[i].Speed )
				ply.WalkSpeed = HumanClass[i].Speed
				ply:SetHealth(HumanClass[i].Health)
				ply.MaxHealth = HumanClass[i].Health
				ply.WeaponSlots = HumanClass[i].WeaponSlots
				ply:SetModel(ChooseRandom(HumanClass[i].M_Model))
				ply.Alert = HumanClass[i].M_Alert
				ply.Death = HumanClass[i].M_Death
				ply.Pain = HumanClass[i].M_Pain
				ply.Taunt = HumanClass[i].M_Taunt
				ply.Burn = HumanClass[i].M_Burn
				ply.Pickup = HumanClass[i].M_Pickup
				ply.Cough = HumanClass[i].M_Cough
				ply.Scared = HumanClass[i].M_Scared
				ply.Heal = HumanClass[i].M_Heal
				ply.Reload = HumanClass[i].M_Reload
				ply.Supply = HumanClass[i].M_Supply
				ply:SetInvSlots(HumanClass[i].InventorySlots)
				ply.DeathType = "normal"
				ply.Class = HumanClass[i].Name
				ply:Give("weapon_inventory")
				for k,v in pairs(HumanClass[i].Primaries) do
					ply:Give(v)
				end
				ply:SelectWeapon(HumanClass[i].Primaries[2])
			end
		end
	end
	ply.Pitch = {95,105}
	if ply.HumanClass != "Support" then
		ply:SetAmmunition("pistol",80)
		ply:SetAmmunition("smg1",60)
		ply:SetAmmunition("buckshot",20)
		ply:SetAmmunition("ar2",60)
	else
		ply:SetAmmunition("pistol",100)
		ply:SetAmmunition("smg1",90)
		ply:SetAmmunition("buckshot",30)
		ply:SetAmmunition("ar2",90)
	end
	ply:SetStamina(100)
	if math.random(1,3) == 1 then
		local item = ents.Create("prop_physics")
		item:SetModel("models/props_lab/binderblue.mdl")
		item:SetPos(ply:GetPos() + Vector(0,0,100))
		item:SetAngles(Angle(0,0,0))
		item:Spawn()
	end
	WriteToFile(string.sub(ply:SteamID(),11),{ply.HumanClass})
end

function GM:ZombieSetup( ply )
	for i=1,#ZombieClass do
		if (string.lower(ZombieClass[i].Name) == string.lower(ply.ZombieClass)) then
			GAMEMODE:SetPlayerSpeed( ply, ZombieClass[i].Speed, ZombieClass[i].Speed )
			ply.WalkSpeed = ZombieClass[i].Speed
			ply:SetModel(ZombieClass[i].Model)
			ply.CorpseModel = ZombieClass[i].Corpse
			ply:SetHealth(ZombieClass[i].Health)
			ply.MaxHealth = ZombieClass[i].Health
			ply.Death = ZombieClass[i].Death
			ply.Pain = ZombieClass[i].Pain
			ply.Taunt = ZombieClass[i].Taunt
			ply.Pitch = ZombieClass[i].Pitch
			ply.DeathType = "normal"
			ply.Class = ZombieClass[i].Name
			ply:Give(ZombieClass[i].Primary)
		end
	end
	ply:DoExtinguish()
	ply:StopPoison()
	ply:StopBleeding()
	ply:StopInfection()
	ply:EmptyInventory()
	ply:SetImmune(true)
	
	local block
	for k, ent in pairs(ents.FindInBox(ply:GetPos() + Vector(-48, -48, 0), ply:GetPos() + Vector(48, 48, 60))) do
		if ent and ent:IsValid() then
			block = ent
		end
	end
	if block then
		if block:IsNPC() then
			GAMEMODE:SpawnUndead(block:GetClass())
			block:Remove()
		else
			block:SetPos(ply:GetPos() + ply:GetForward() * 90)
		end
	end
end

function GM:PlayerSpawn( ply )
	if ply.ZombieClass == "Dead" then
		ply.ZombieClass = "Undead"
		ply:SetTeam(TEAM_UNDEAD)
		//zombie class hint
		timer.Simple(5, function(ply) Inventory.Notice(ply, "Press F3 to change your class", Inventory.White) end, ply )
		timer.Simple(10, function(ply) Inventory.Notice(ply, "Kill humans to earn upgrades", Inventory.White) end, ply )
	end
	ply:ShouldDropWeapon(false)
	ply:UnSpectate()
	if ply:Team() == TEAM_HUMANS then
		ply:StripWeapons()
		GAMEMODE:HumanSetup(ply)	
		ply:ClientSound(ChooseRandom(GameSounds.OpeningMusic))
		ply:SetImmune(false)
		ply:SetNetworkedVector("antidotepos",GameInfo.Cure:LocalToWorld(GameInfo.Cure:OBBCenter()))
		umsg.Start("UpdateCureTime",v)
		umsg.Short( CurTime() + (GameInfo.LocationInterval*60) )
		umsg.End() 
	elseif ply:Team() == TEAM_UNDEAD then
		GAMEMODE:ZombieSetup(ply)	
	else
		ply.MaxHealth = 100
	end
	umsg.Start("MaxHealth", ply)
	umsg.Short( ply.MaxHealth )
	umsg.End() 
end

function GM:SetupMove(ply, mv)
	if not ply:Alive() then return end
	if ply:Team() == TEAM_HUMANS then
		if ply == nil or !ply:Alive() then 
			return
		end
		if ply:KeyDown( IN_SPEED ) and mv:GetForwardSpeed() > 20 then 
			if ply:GetStamina() <= GameInfo.StaminaRegen then
				ply:SetRunSpeed( ply.WalkSpeed )
			else
				if ply:KeyDown( IN_DUCK ) or ply:KeyDown( IN_WALK ) then
					ply:SetRunSpeed( ply.WalkSpeed - 90 )
				else
					ply:SetRunSpeed( ply.WalkSpeed + 120 )
				end
			end
			return
		elseif ply:KeyDown( IN_WALK ) then
			ply:SetWalkSpeed( ply.WalkSpeed - 95 )
			return
		elseif ply:KeyDown( IN_BACK ) then
			ply:SetWalkSpeed( ply.WalkSpeed - 80 )
			return
		elseif ply:KeyDown( IN_DUCK ) then
			ply:SetWalkSpeed( ply.WalkSpeed - 75 )
			return
		else
			ply:SetWalkSpeed( ply.WalkSpeed )
			return
		end
	elseif ply:Team() == TEAM_UNDEAD then //zombie players
		if ply == nil or !ply:Alive() then 
			return
		end
		if ply:KeyDown( IN_BACK ) then
			mv:SetMaxClientSpeed( ply.WalkSpeed - 40 )
			return
		else
			mv:SetMaxClientSpeed( ply.WalkSpeed )
			return
		end
	else //spec
		ply:SetWalkSpeed( 10 )
		ply:SetRunSpeed( 10 )
	end
end

function GM:PlayerUse(ply, entity)
	if not entity then return end
	if not entity:IsValid() then return end
	if ply:Team() == TEAM_UNDEAD then
		if entity:GetClass() == "prop_door_rotating" or entity:GetClass() == "func_ladder" 
		or entity:GetClass() == "func_button" or entity:GetClass() == "func_useableladder" then
			return false
		end
	end
	return true
end

function GM:KeyRelease( ply, key )
	if key == IN_USE and ply:Team() == TEAM_HUMANS then
		if not ply.TraceTimer then
			Inventory.PlayerTrace(ply)
			ply.TraceTimer = CurTime() + .3
		elseif ply.TraceTimer < CurTime() then
			Inventory.PlayerTrace(ply)
			ply.TraceTimer = CurTime() + .3
		end
	end
end

function GM:ShowHelp(ply)
	if ply.HelpMenu then return end
	umsg.Start("ShowHelp", ply)
	umsg.End() 
	ply.HelpMenu = true
end

function CloseHelpMenu(ply,cmd,args)
	ply.HelpMenu = false
end
concommand.Add("closehelpmenu", CloseHelpMenu)	

function GM:ShowSpare1(ply)
	if GameInfo.GameOver or ply.InMenu then return end
	ply.MenuTimer = ply.MenuTimer or CurTime() - 1
	if ply:Team() == TEAM_HUMANS and ply.MenuTimer < CurTime() then
		umsg.Start("MerchantMenu", ply)
		umsg.End() 
		ply.MenuTimer = CurTime() + 2
		ply.InMenu = true
		ply:ClientSound(ChooseRandom(GameSounds.RadioBeep))
	elseif ply:Team() == TEAM_UNDEAD and ply.MenuTimer < CurTime() and ply:Alive() then
		ply.MenuTimer = CurTime() + 5
		umsg.Start("ClassMenu", ply)
		umsg.Short(TEAM_UNDEAD)
		umsg.End() 
		ply.MenuOpen = true
	end
end

function GM:ShowSpare2(ply)
	ply.MenuTimer = ply.MenuTimer or CurTime() - 1
	if ply.MenuTimer < CurTime() and ply:Alive() then
		ply.MenuTimer = CurTime() + 5
		umsg.Start("OptionsMenu", ply)
		umsg.End() 
		ply.MenuOpen = true
	end
end

function GM:ShowTeam(ply)
	if not ply.AutoBuy then
		Inventory.Notice(ply, "Hit F2 again to buy item: "..ply.LastItem, Inventory.Blue)
		timer.Create("ShowTeam"..ply:Name(), 2.5, 1, function(ply) ply:ResetAutoBuy() end, ply)
		ply.AutoBuy = CurTime() + 2.5
	else
		if Inventory.MerchantTrace(ply) then
			ply:ResetAutoBuy()
			BuyItem(ply,1,{ply.LastItem})
		else
			Inventory.Notice(ply,"You need to be outdoors",Inventory.Red)
			ply:ClientSound(ChooseRandom(GameSounds.Deny))
		end
	end
end

function GM:PlayerDisconnected(ply)
	ply:SetTeam(TEAM_UNDEAD)
	table.insert(GameInfo.Disconnected,ply:SteamID())
	if team.NumPlayers(TEAM_HUMANS) < 1 and GameInfo.Unlock > 0.2 then
		GAMEMODE:EndGame()
	end
end

function GM:PlayerDeathSound()
	return true
end

function GM:EntityTakeDamage( ent, inflictor, attacker, amount, dmginfo )
	if not ent:IsPlayer() then return end
	if not ent:Alive() then return end
	if ent.ZombieClass == "Dead" then dmginfo:SetDamage(0) return end
	
	if string.find(attacker:GetClass(),"bomb") then
		dmginfo:SetDamage(10)
	end
	
	if attacker:IsWorld() or dmginfo:GetAttacker():IsWorld() then
		if ent:Team() == TEAM_UNDEAD then
			dmginfo:SetDamage(10)
		end
	end

	if ent:Team() == TEAM_HUMANS and ent:Health() < 30 then
		if attacker:IsPlayer() or attacker:IsWorld() then
			ent.DeathType = "normal"
		end
	end
	
	if attacker and attacker != NULL and attacker != nil then
		if attacker:IsValid() then
			local aclass = attacker:GetClass()
			if string.find(aclass, "prop_") then
				dmginfo:SetDamage(10)
				ent:SetVelocity(Vector(0,0,0))
			elseif string.find(aclass,"npc_") then
				ent.DeathType = "normal"
				ent:VoiceSound(ChooseRandom(ent.Pain),100,.1)
				umsg.Start("BloodStain", ent)
				umsg.End() 
			elseif string.find(aclass,"molotov") then
				ent:VoiceSound(ChooseRandom(ent.Pain),100,.1)
			end
		end
	end
end


function GM:ScalePlayerDamage( ply, group, dmginfo )
	if ply:Team() == TEAM_HUMANS then return dmginfo end
	if not dmginfo:GetAttacker():IsPlayer() then return dmginfo end
	ply.DeathType = "normal"
	if ply:Team() == TEAM_UNDEAD then
		umsg.Start("BloodStain", ply)
		umsg.End() 
	end
	if group == HITGROUP_HEAD then
		dmginfo:ScaleDamage(2.2)
		ply:AddAttackerDamage(dmginfo:GetAttacker(),dmginfo:GetDamage(),true)
	elseif group == HITGROUP_CHEST then
		dmginfo:ScaleDamage(.7)
		ply:AddAttackerDamage(dmginfo:GetAttacker(),dmginfo:GetDamage(),false)
	elseif group == HITGROUP_STOMACH then
		dmginfo:ScaleDamage(.55)
		ply:AddAttackerDamage(dmginfo:GetAttacker(),dmginfo:GetDamage(),false)
	else
		dmginfo:ScaleDamage(.3)
		ply:AddAttackerDamage(dmginfo:GetAttacker(),dmginfo:GetDamage(),false)
	end
return dmginfo end

function GM:ScaleNPCDamage( npc, group, dmginfo )
	if not dmginfo:GetAttacker():IsPlayer() then return dmginfo end
	if group == HITGROUP_HEAD then
		dmginfo:ScaleDamage(2.2)
		npc:AddAttackerDamage(dmginfo:GetAttacker(),dmginfo:GetDamage(),true)
		local attacker = dmginfo:GetAttacker()
		if not attacker.HeadsHint then
			if math.random(1,3) == 1 then
				attacker.HeadsHint = true
				Inventory.Notice(attacker, "Headshot combos will earn you bonus "..GameInfo.PointName.."s", Inventory.White)
			end
		end
	elseif group == HITGROUP_CHEST then
		dmginfo:ScaleDamage(.7)
		npc:AddAttackerDamage(dmginfo:GetAttacker(),dmginfo:GetDamage(),false)
	elseif group == HITGROUP_STOMACH then
		dmginfo:ScaleDamage(.55)
		npc:AddAttackerDamage(dmginfo:GetAttacker(),dmginfo:GetDamage(),false)
	else
		dmginfo:ScaleDamage(.3)
		npc:AddAttackerDamage(dmginfo:GetAttacker(),dmginfo:GetDamage(),false)
	end
return dmginfo end

function GM:CalculateKiller(victim,killer,dmginfo) //OBSOLETE
	
	local tbl = victim:GetAttackerTable()
	if tbl == {} then return end
	local dmg,headshot = victim:FindAttackerDamage(killer)
	
	if not killer or not killer:IsValid() then return end
	
	if GameInfo.KillCalculator == "lastkiller" then
		killer:AddAwardPoints(AWARD_GOOD_KILLS,1)
		killer:AddAwardPoints(AWARD_BAD_KILLS,1)
		if headshot then
			//killer:AddPoints(Points[victim.ZombieClass] + 1)
			victim:SpawnPlutonium(Points[victim.ZombieClass] + 1,killer)
			killer:AddAwardPoints(AWARD_GOOD_HEADSHOT,1)
			killer:AddAwardPoints(AWARD_BAD_HEADSHOT,1)
		else
			//killer:AddPoints(Points[victim.ZombieClass])
			victim:SpawnPlutonium(Points[victim.ZombieClass],killer)
		end
	elseif GameInfo.KillCalculator == "mostdamage" then
		local winnar = killer
		local highest = 1
		for k,v in pairs(tbl) do 
			if v.Damage > highest then
				highest = v.Damage
				winnar = v.Player
				headshot = v.Headshot
			end
		end
		winnar:AddAwardPoints(AWARD_GOOD_KILLS,1)
		winnar:AddAwardPoints(AWARD_BAD_KILLS,1)
		if headshot then
			//winnar:AddPoints(Points[victim.ZombieClass] + 1)
			victim:SpawnPlutonium(Points[victim.ZombieClass] + 1,winnar)
			winnar:AddAwardPoints(AWARD_GOOD_HEADSHOT,1)
			winnar:AddAwardPoints(AWARD_BAD_HEADSHOT,1)
		else
			//winnar:AddPoints(Points[victim.ZombieClass])
			victim:SpawnPlutonium(Points[victim.ZombieClass],winnar)
		end
	else //points are shared
		local minimum = 20
		for k,v in pairs(tbl) do 
			//Msg("PLAYER:"..v.Player:Name().." DMG:"..v.Damage.." HS:"..tostring(v.Headshot).."\n")
			if v.Damage > minimum or killer == v.Player then
				if v.Headshot then
					//v.Player:AddPoints(Points[victim.ZombieClass] + 1)
					victim:SpawnPlutonium(Points[victim.ZombieClass] + 1,v.Player)
					v.Player:AddAwardPoints(AWARD_GOOD_HEADSHOT,1)
					v.Player:AddAwardPoints(AWARD_BAD_HEADSHOT,1)
				else
					//v.Player:AddPoints(Points[self.Name])
					victim:SpawnPlutonium(Points[victim.ZombieClass],v.Player)
				end
				v.Player:AddAwardPoints(AWARD_GOOD_KILLS,1)
				v.Player:AddAwardPoints(AWARD_BAD_KILLS,1)
			end
		end
	end
end

function GM:PlayerDeathThink( ply )

	if ply.RespawnTime and ply.RespawnTime < CurTime() then 
		ply:Spawn()
	else
		return
	end

	if ( ply:KeyPressed( IN_ATTACK ) || ply:KeyPressed( IN_ATTACK2 ) || ply:KeyPressed( IN_JUMP ) ) then
		ply:Spawn()
	end
	
	if not ply:GetNetworkedEntity("corpse"):IsValid() then
		ply:Spawn()
	end
	
end

function GM:DoPlayerDeath(ply, attacker, dmginfo)

	ply.RespawnTime = CurTime() + 8
	
	ply:Flashlight(false)
	ply:Freeze(false)
	ply:VoiceSound(ChooseRandom(ply.Death),100)
	
	// All we need is to spawn the corpse/gun/effects.
	local ent = ents.Create( "prop_ragdoll" )
	ent:SetKeyValue("origin", ply:GetPos().x.." "..ply:GetPos().y.." "..ply:GetPos().z + 1)
	ent:SetKeyValue("angles", ply:GetAngles().p + math.random(-3,3).." "..ply:GetAngles().y +  math.random(-3,3).." "..ply:GetAngles().r + math.random(-3,3))
		
	local dtype = ply.DeathType
	if dtype == "burn" or dtype == "explosion" then
		if ply:Team() == TEAM_HUMANS then
			ent:SetModel(ChooseRandom(PlayerGibs))
		else
			if ply.CorpseModel then
				ent:SetModel(ply.CorpseModel)
			else
				ent:SetModel(ply:GetModel())
			end
		end
	else
		if ply.CorpseModel then
			ent:SetModel(ply.CorpseModel)
		else
			ent:SetModel(ply:GetModel())
		end
	end
			
	ent:Spawn()
	ent:Activate() 
	ent:SetCollisionGroup( COLLISION_GROUP_WEAPON )
	ent:Fire("kill",1,math.Clamp(GameInfo.RagdollDieTime,10,90))
	ply:SetNetworkedEntity("corpse",ent)
	
	for i=1,128 do
		local bone = ent:GetPhysicsObjectNum( i )
		if ValidEntity( bone ) then
			local bonepos, boneang = ent:GetBonePosition( ent:TranslatePhysBoneToBone( i ) )
			bone:SetPos( bonepos )
			bone:SetAngle( boneang )
		end
	end

	if dtype == "burn" or dtype == "explosion" then
		local effectdata = EffectData()
		effectdata:SetEntity(ent)
		util.Effect("corpse_fire", effectdata)
	end
		
	if ply:Team() == TEAM_UNDEAD and attacker != ply and attacker != nil and attacker:IsPlayer() then
		ply:SpawnPlutonium(1,attacker)
		if ply.ZombieClass == "Disease" then
			ply:SpawnGasClouds(ent:GetPos() + Vector(0,0,5))
		end
		ply:DoExtinguish()
		//GAMEMODE:CalculateKiller(ply,attacker,dmginfo) OBSOLETE
	end
	
	if ply:Team() == TEAM_HUMANS then	
		//make all this shit stop
		ply:DoExtinguish()
		ply:StopPoison()
		ply:StopBleeding()
		ply:StopInfection()
		
		//make them zombies
		ply.ZombieClass = "Dead"
		ply:SetTeam(TEAM_UNDEAD)
		
		for k,v in pairs(team.GetPlayers(TEAM_HUMANS)) do
			if v:GetPos():Distance(ply:GetPos()) < 900 and v != ply then
				v:VoiceSound(ChooseRandom(v.Scared),100,math.Rand(0,3))
			end
		end
		if team.NumPlayers(TEAM_UNDEAD) < 1 then
			ply:AddPowerup(math.random(1,3))
		end
		if attacker:IsPlayer() then
			if attacker:Team() == TEAM_UNDEAD then
				attacker:AddPowerup()
			end
		end
		
		local num = math.floor(ply:GetPoints() / 25)
		if num >= 1 then
			timer.Simple(5, function(ply,num) 
			Inventory.Notice(ply, "Your "..GameInfo.PointName.."s were converted to upgrades", Inventory.White)
				for i=1,num do
					ply:AddPowerup()
				end 
			end, ply, num )
		end
		
		ply:ClientSound(ChooseRandom(GameSounds.DeathMusic))
		ply:SetNetworkedString("available","99")
		if team.NumPlayers(TEAM_HUMANS) < 1 and table.Count(player.GetAll()) >= 2 then
			GAMEMODE:EndGame()
		end
	end
			
	local phys = ent:GetPhysicsObject()
	if phys:IsValid() then
		phys:SetMass(50)
	end
				
	local fnum = 700
	if dmginfo:IsExplosionDamage() or dtype == "explosion" then 
		fnum = phys:GetMass()^3.2
	elseif GameInfo.ExaggeratedDeath then
		fnum = phys:GetMass()^2.3 // i dew it foar the lols
	else
		fnum = phys:GetMass()^2
	end
				
	if attacker != nil and attacker:IsPlayer() then
		local dir = (ent:GetPos() - attacker:GetPos() + (VectorRand() * 2)):Normalize()
		phys:ApplyForceCenter(attacker:GetAimVector() * fnum) //(dir * fnum)
		phys:AddAngleVelocity(Angle(math.random(-1*(fnum/3),fnum/3),math.random(-1*(fnum/2),fnum/2),math.random(-1*(fnum/3),fnum/3)))
				
		if attacker:IsPlayer() then
			local taunt = math.random(1,3)
			if taunt == 1 and attacker:Alive() then
				attacker:VoiceSound(ChooseRandom(attacker.Taunt),100,math.Rand(1,1.5))
			end
		end
	else
		local dir = VectorRand():Normalize()
		local vel = ply:GetVelocity()
		phys:ApplyForceCenter((dir * (fnum/3)) + vel)
		local ang = Angle(math.random(-1*(fnum/4),fnum/4),math.random(-1*(fnum/4),fnum/4),math.random(-1*(fnum/4),fnum/4))
		phys:AddAngleVelocity(ang)
	end
		
	if ply.ZombieClass == "Dead" then
		local items = ply:GetItemInventory()
		local madeitem = false
		for k,v in pairs(items) do
			local tbl = Inventory.FindTable(v)
			if tbl.Weight <= 2 then
				if not madeitem then
					madeitem = true
					local hpos = ent:LookupAttachment("anim_attachment_LH")
					local att = ent:GetAttachment(hpos)
					if att == nil then
						att = {}
						att.Ang = Angle(0,0,0)
						att.Pos = ent:GetPos() + Vector(0,0,5)
					end
					local item = ents.Create("prop_physics")
					item:SetModel(tbl.Model)
					item:SetPos(att.Pos)
					item:SetAngles(att.Ang)
					item:Spawn()
					item:SetHealth(150)
				end
			end
		end
		madeitem = false
		local weps = ply:GetWeapons()
		local allowed = {"smg1","ar2","pistol","buckshot"}
		for k,v in pairs(weps) do
			if not madeitem then
				if table.HasValue(allowed,v:GetUsedAmmoType()) then
					madeitem = true 
					local hpos = ent:LookupAttachment("anim_attachment_RH")
					local att = ent:GetAttachment(hpos)
					if att == nil then
						att = {}
						att.Ang = Angle(0,0,0)
						att.Pos = ent:GetPos() + Vector(0,0,5)
					end
					local item = ents.Create("sent_weapon")
					item:SetModel(v:GetWorldModel())
					item:SetPos(att.Pos)
					item:SetAngles(att.Ang)
					item:Spawn()
				end
			end
		end
	end	
end

function GM:ForceSpawn(ply)
	if !ply:IsValid() or ply:Alive() then return end
	ply:Spawn()
end

function GM:CanPlayerSuicide( ply )
	Inventory.Notice(ply,"Suicide is not the answer",Inventory.Red)
	return false//true
end 	

function GM:PlayerTraceAttack( ply, dmginfo, dir, trace )

	local attacker = dmginfo:GetAttacker()
	
	if !attacker:IsValid() or attacker == ply then
		return
	end
	
	if attacker:Team() != ply:Team() and ply:Health() > 50 then
		ply.DeathType = "normal"
		if math.random(1,5) == 1 then
			if SERVER then
				ply:VoiceSound(Sound(ChooseRandom(ply.Pain)),100,math.random(95,105))
			end
		end
	end
	
	util.Decal("Blood", trace.HitPos + trace.HitNormal, trace.HitPos - trace.HitNormal)
	
	local effectdata = EffectData()
	effectdata:SetOrigin(trace.HitPos)
	effectdata:SetNormal(trace.HitNormal)
	util.Effect("bodyshot", effectdata)

	return self.BaseClass:PlayerTraceAttack(ply, dmginfo, dir, trace)
end

function VoiceChat(ply,key)
	if ply:Team() == TEAM_UNDEAD then return end
	ply.ChatTimer = ply.ChatTimer or 0
	if key == 0 then
		if ply.ChatTimer < CurTime() then
			ply.ChatTimer = CurTime() + 4
			timer.Simple(1, function(ply) Inventory.Notice(ply, "Press NumPad 1 to call for help", Inventory.White) end, ply )
			timer.Simple(3, function(ply) Inventory.Notice(ply, "Press NumPad 2 to spot zombies", Inventory.White) end, ply )
			timer.Simple(5, function(ply) Inventory.Notice(ply, "Press NumPad 3 to signal retreat", Inventory.White) end, ply )
		end
	end
	if ply.ChatTimer < CurTime() then
		ply.ChatTimer = CurTime() + 4
		if key == 1 then
			if ply:IsFemale() then
				ply:VoiceSound(ChooseRandom(GameInfo.Fem1),100)
			else
				ply:VoiceSound(ChooseRandom(GameInfo.Male1),100)
			end
			ply:ConCommand("say "..ChooseRandom({"I need assistance!","I'm over here!","Help me!","I need backup"}))
		elseif key == 2 then
			if ply:IsFemale() then
				ply:VoiceSound(ChooseRandom(GameInfo.Fem2),100)
			else
				ply:VoiceSound(ChooseRandom(GameInfo.Male2),100)
			end
			ply:ConCommand("say "..ChooseRandom({"I see zombies!","Incoming!","Take cover!","Here they come!"}))
		else
			if ply:IsFemale() then
				ply:VoiceSound(ChooseRandom(GameInfo.Fem3),100)
			else
				ply:VoiceSound(ChooseRandom(GameInfo.Male3),100)
			end
			ply:ConCommand("say "..ChooseRandom({"We should keep moving","Run away!","Follow me!","Come with me"}))
		end
	end
end
numpad.Register( "VoiceChat", VoiceChat ) 
	
function BuyItem(ply,cmd,args)
	if args[1] == nil then ply.InMenu = false return end
	ply.InMenu = false
	if ply:Team() == TEAM_HUMANS then
		local tbl = nil
		for k,v in pairs(Inventory.Merchant) do
			if v.Name == args[1] then
				tbl = table.Copy(v)
			end
		end
		if not ply:HasPoints(tbl.Price) or tbl == nil then
			ply:ClientSound(ChooseRandom(GameSounds.Deny))
			Inventory.Notice(ply, "You can't afford that item", Inventory.Red)
			return
		end
		if tbl.Stock < 1 then
			ply:ClientSound(ChooseRandom(GameSounds.Deny))
			Inventory.Notice(ply, "That item is not in stock", Inventory.Red)
			return
		end
		if tbl.Weapon then
			if Inventory.WeaponCount(ply) < ply.WeaponSlots then
				local weps = ply:GetWeapons()
				for k,v in pairs(weps) do
					if v:GetWorldModel() == tbl.Model then
						Inventory.Notice(ply, "You already have that gun", Inventory.Red)
						ply:ClientSound(ChooseRandom(GameSounds.Deny))
						return
					end
				end
				
				local atypes = {"_tp","_sg","_sh","_ar"}
				local names = {"pistol","smg1","buckshot","ar2"}
				local amts = {20,30,12,30}
				for k,v in pairs(atypes) do
					if string.find(tbl.Weapon,v) then
						ply:AddAmmunition(names[k],amts[k])
					end
				end
				
				ply:Give(tbl.Weapon)
				Inventory.Notice(ply, "Purchased gun: "..tbl.Name, Inventory.Blue)
				if ply:HasInvItem("models/props/de_prodigy/desk_console1b.mdl") 
				or ply:HasInvItem("models/props/cs_office/phone.mdl") 
				or ply:HasInvItem("models/props/cs_office/phone_p2.mdl") then
					ply:TakePoints(tbl.Price - 5)
					Inventory.Notice(ply, "You were discounted 5 "..GameInfo.PointName.."s", Inventory.Yellow)
				else
					ply:TakePoints(tbl.Price)
				end
				ply:AddAwardPoints(AWARD_GOOD_POINTS,tbl.Price)
				ply:AddAwardPoints(AWARD_BAD_POINTS,tbl.Price)
				ply:ClientSound(ChooseRandom(GameSounds.Cash))
				Inventory.ReduceStock(tbl.Name)
				ply:SelectWeapon(tbl.Weapon)
			else
				Inventory.Notice(ply, "You need to drop a gun first", Inventory.Red)
				ply:ClientSound(ChooseRandom(GameSounds.Deny))
			end
		elseif tbl.Ammo then
			ply:AddAmmunition(tbl.Ammo,tbl.Weight)
			Inventory.Notice(ply, "Purchased ammo: "..tbl.Name, Inventory.Blue)
			ply:TakePoints(tbl.Price)
			ply:AddAwardPoints(AWARD_GOOD_POINTS,tbl.Price)
			ply:AddAwardPoints(AWARD_BAD_POINTS,tbl.Price)
			ply:ClientSound(ChooseRandom(GameSounds.Cash))
			ply:EmitSound(Inventory.ItemPickup,100,math.random(90,100))
			Inventory.ReduceStock(tbl.Name)
			ply.LastItem = tbl.Name
		elseif tbl.Weight then
			if tbl.Name == "Mystery Item" then
				local lol = ChooseRandom(Inventory.MysteryItems)
				local canhave = ply:CanAddToInventory(lol)
				if canhave then
					local prop = ents.Create("prop_physics")
					prop:SetPos(ply:GetPos() + Vector(0,0,100))
					prop:SetModel(lol)
					prop:Spawn()
					prop:SetColor(0,0,0,0)
					prop:SetHealth(150)
					if Inventory.CheckItem(ply,prop) then
						ply:AddToInventory(prop)
						Inventory.Notice(ply, "Purchased item: "..tbl.Name, Inventory.Blue)
						ply:TakePoints(tbl.Price)
						ply:ClientSound(ChooseRandom(GameSounds.Cash))
						ply:SynchInventory()
						ply:ConCommand("invnext")
					end
					Inventory.ReduceStock(tbl.Name)
				else
					ply:ClientSound(ChooseRandom(GameSounds.Deny))
					//Inventory.Notice(ply, "You can't carry that item", Inventory.Red)
				end
				return
			end
			local canhave = ply:CanAddToInventory(tbl.Model)
			if canhave then
				local prop = ents.Create("prop_physics")
				prop:SetPos(ply:GetPos() + Vector(0,0,100))
				prop:SetModel(tbl.Model)
				prop:Spawn()
				prop:SetColor(0,0,0,0)
				prop:SetHealth(150)
				ply:AddToInventory(prop)
				Inventory.Notice(ply, "Purchased item: "..tbl.Name, Inventory.Blue)
				ply:TakePoints(tbl.Price)
				ply:AddAwardPoints(AWARD_GOOD_POINTS,tbl.Price)
				ply:AddAwardPoints(AWARD_BAD_POINTS,tbl.Price)
				ply:ClientSound(ChooseRandom(GameSounds.Cash))
				Inventory.ReduceStock(tbl.Name)
				ply.LastItem = tbl.Name
			else
				ply:ClientSound(ChooseRandom(GameSounds.Deny))
				//Inventory.Notice(ply, "You can't carry that item", Inventory.Red)
			end
		end
	end
end
concommand.Add("buyitem", BuyItem)	
	
function ChangeClass(ply,cmd,args)
	if not ply.MenuOpen then return end
	if args[1] == nil then return end
	//if not string.find(ply:GetNetworkedString("available","12"),tostring(args[2])) and ply:Team() == TEAM_HUMANS then Inventory.Notice(ply,"Nice try",Inventory.Red) return end
	if ply:Team() == TEAM_SPECTATOR then
		ply:SetTeam(TEAM_HUMANS)
		if table.HasValue({"Medic","Engineer","Militant","Support"},tostring(args[1])) then
			ply.HumanClass = tostring(args[1])
		else
			ply.HumanClass = "Militant"
			ply.ZombieClass = "Dead"
			ply:Kill()
			return
		end
		ply:Spawn()
		for k,v in pairs(player.GetAll()) do
			v:ChatPrint(ply:Name().." chose class: "..ply.HumanClass)
		end
		umsg.Start("UpdateTime", ply)
		umsg.Short( GameInfo.EndTime )
		umsg.End() 
	elseif ply:Team() == TEAM_UNDEAD then
		if table.HasValue({"Ghoul","Wretch","Undead","Disease","Banshee"},tostring(args[1])) then
			ply.ZombieClass = tostring(args[1])
		else
			ply.ZombieClass = "Undead"
		end
		ply:Kill()
	end
	ply.MenuOpen = false
	ply:EmitSound(ChooseRandom(GameSounds.WindGusts))
end
concommand.Add("changeclass", ChangeClass)

function DebugMsg(txt)
	for k,v in pairs(player.GetAll()) do
		v:ChatPrint("DEBUG: "..tostring(txt))
	end
return txt end

function DebugTrace()
	player.GetByID(1):ChatPrint("debug trace:")
	local trace = util.GetPlayerTrace(player.GetByID(1))
	local tr = util.TraceLine(trace) 
	DebugMsg("model: "..tr.Entity:GetModel().." - class: "..tr.Entity:GetClass().." - physbone: "..tr.PhysicsBone)
end